{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Мелкие нововведения C++11 / C++14 / C++17 / C++20"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### auto (since C++11)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "https://en.cppreference.com/w/cpp/language/auto"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`auto` - синтаксический сахар, указывающий компилятору вывести тип самостоятельно."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "По каким правилам компилятор выводит тип?\n",
    "\n",
    "> in the type specifier of a variable: `auto x = expr;`. The type is deduced from the initializer. If the placeholder type specifier is auto, the variable type is deduced from the initializer using the rules for template argument deduction from a function call (see template argument deduction#Other contexts for details). \n",
    "\n",
    "Компилятор использует правила вывода типов у аргументов шаблонов:\n",
    "\n",
    "https://en.cppreference.com/w/cpp/language/template_argument_deduction\n",
    "\n",
    "**Упрощённо**: у типа выражения убираются квалификаторы `const`, `&` и `&&` (более строгое определение - по ссылке)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Упражнение**:\n",
    "    \n",
    "Какой тип будет у переменной?\n",
    "\n",
    "```c++\n",
    "int x;\n",
    "\n",
    "// 1\n",
    "auto y = x;  // ???\n",
    "\n",
    "// 2:\n",
    "int& a = x;\n",
    "auto y = a;  // ???\n",
    "\n",
    "// 3:\n",
    "const int& a = x;\n",
    "auto y = a;  // ???\n",
    "\n",
    "// 4:\n",
    "const std::string& get_name();\n",
    "auto name        = get_name();  // ???\n",
    "auto& name       = get_name();  // ???\n",
    "const auto& name = get_name();  // ???\n",
    "\n",
    "// 5:\n",
    "std::string& get_name();\n",
    "auto name        = get_name();  // ???\n",
    "auto& name       = get_name();  // ???\n",
    "const auto& name = get_name();  // ???\n",
    "\n",
    "// 6:\n",
    "std::string get_name();\n",
    "auto name        = get_name();  // ???\n",
    "auto& name       = get_name();  // ???\n",
    "const auto& name = get_name();  // ???\n",
    "\n",
    "// 7:\n",
    "auto v       = std::vector<int>{1, 2, 3};  // ???\n",
    "const auto v = std::vector<int>{1, 2, 3};  // ???\n",
    "\n",
    "// 8:\n",
    "auto v = {1, 2, 3};  // ???\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Рекомендации по использованию:**\n",
    "\n",
    "По вопросу использования `auto` мир С++ раскололся на две секты:\n",
    "* google coding guidelines - минимизация `auto`\n",
    "* Herb Sutter (и многие другие) - AAA (almost always auto)\n",
    "\n",
    "Разберём, какие аргументы стоят за этими спорами."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__Google coding guidelines__ рекомендуют использовать `auto` только там, где:\n",
    "* тип очевиден по выражению справа (прямо по тексту справа)\n",
    "* тип - итератор коллекции\n",
    "\n",
    "```c++\n",
    "auto processor = std::make_unique<LogsProcessor>();  // тип очевиден по выражению справа\n",
    "auto dog = dynamic_cast<Dog>(animal);                // тип очевиден по выражению справа\n",
    "auto it = std::begin(v);                             // итератор, чтобы не писать std::vector<int>::iterator\n",
    "\n",
    "std::string name = get_name();  // из выражения справа не очевиден тип, надо подглядывать в определение get_name()\n",
    "std::string full_name = name + \" \" + surname;  // из выражения справа не очевиден тип, надо смотреть типы name, surname\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Almost always auto**\n",
    "* Пишите `auto` везде где это помогает сократить код или подняться на уровень абстракции\n",
    "\n",
    "```c++\n",
    "auto logs_processor = create_logs_processor();\n",
    "auto logs = logs_processor->run(\"logs.txt\");\n",
    "auto request_future = send_logs_to_server(std::move(logs));\n",
    "```\n",
    "\n",
    "Идея, стоящая за подходом: в первую очередь важна логика алгоритма, тип объектов вторичен. Логика алгоритма - более высокий уровень абстракции кода.\n",
    "\n",
    "Сравните:\n",
    "\n",
    "```c++\n",
    "std::shared_ptr<LogsProcessor> logs_processor = create_logs_processor();\n",
    "std::vector<LogRecord> logs = logs_processor->run(\"logs.txt\");\n",
    "std::future<LogsReceiveResponse> request_future = send_logs_to_server(std::move(logs));\n",
    "```\n",
    "\n",
    "Замечания:\n",
    "\n",
    "* Конечно, когда дело доходит до специфики С++, тип объектов важен. И тут на помощь приходят нормальные IDE. Т.е. задача красиво показать читателю тип - это задача IDE, а текст отражает более высокий уровень абстракции.\n",
    "* Всякие `int` и особенно `bool`, скорее всего, не стоит заменять на `auto`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Кто лучше?**\n",
    "\n",
    "* Almost always auto - код меньше по объёму и алгоритм проще для чтения, но нужна нормальная IDE.\n",
    "* Google coding guidelines - код читать труднее, но глазами видно ошибки с типами.\n",
    "\n",
    "**Замечание**: Google работает с монорепозиторием (много, очень много кода в одном git-репозитории), который ни в одну IDE-шку не влезет. Поэтому его guidelines для самого Google логичны. Если кто-то на вполне нормальном проекте выбирает подход google coding guidelines для `auto` просто потому что Google умный и ему виднее, то так делать не надо."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**raw pointers**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Отдельная рекомендация, для raw pointers использовать `auto*`. Это связано с частым нарушением ошибок владения в языке С++.\n",
    "\n",
    "```c++\n",
    "std::unique_ptr<LogsProcessor> create_logs_processor();\n",
    "LogsProcessor* create_logs_processor();  // да, это плохой С++, но его уже много\n",
    "\n",
    "auto logs_processor = create_logs_processor();  // for shared_ptr\n",
    "auto* logs_processor = create_logs_processor();  // for raw pointer\n",
    "\n",
    "\n",
    "auto* network_subsystem = NetworkSubsystem::find(app_context);\n",
    "if (network_subsystem)\n",
    "    network_subsystem->send_request(r);\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### auto return type"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "auto return type - просьба компилятору самостоятельно вывести тип возвращаемого значения:\n",
    "\n",
    "```c++\n",
    "auto create_logs_processor()\n",
    "{\n",
    "    const auto settings = read_log_settings();\n",
    "    return std::make_shared<LogsProcessor>(settings);\n",
    "}\n",
    "// компилятор автоматически выведет std::shared_ptr<LogsProcessor>\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Замечание 1:** auto return type можно использовать только в тех случаях, когда компилятор может вывести тип. В примере выше - можно. А вот так - нельзя. Почему?\n",
    "\n",
    "```c++\n",
    "// LogsProcessor.h\n",
    "auto create_logs_processor();\n",
    "\n",
    "// LogsProcessor.cpp\n",
    "auto create_logs_processor()\n",
    "{\n",
    "    const auto settings = read_log_settings();\n",
    "    return std::make_shared<LogsProcessor>(settings);\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Замечание 2**: споры, использовать или нет auto return type - аналогичны спорам, использовать или нет `auto`. Если у вас хорошая IDE, которая всё нужное подсветит, почему бы и нет. Если вы работаете в Google и программируете в блокноте, то, наверное, не стоит."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### auto + trailing return type"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Конструкция вида:\n",
    "    \n",
    "```c++\n",
    "auto create_logs_processor() -> std::shared_ptr<LogsProcessor>;\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Мне сложно сказать, почему её любят использовать и resharper, например, усиленно рекомендует писать именно в таком стиле.\n",
    "\n",
    "Есть одно преимущество: для методов, возвращающих объект с типом из пространства класса, можно не указывать класс. Пример:\n",
    "\n",
    "```c++\n",
    "class LogsProcessor\n",
    "{\n",
    "public:\n",
    "    struct ProcessedItem { /*...*/ };\n",
    "    \n",
    "    ProcessedItem* last_processed() const;\n",
    "};\n",
    "\n",
    "// standard:\n",
    "LogsProcessor::ProcessedItem* LogsProcessor::last_processed() const\n",
    "{ /*...*/ }\n",
    "\n",
    "// trailing return type:\n",
    "auto LogsProcessor::last_processed() const -> ProcessedItem*\n",
    "{ /*...*/ }\n",
    "```\n",
    "\n",
    "Не нахожу рациональным рекомендовать вам новую непривычную конструкцию, которая чаще добавляет объём кода без особых улучшений. Не знаю, почему resharper рекомендует такой подход."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Бывает хитрая шаблонная магия, где без trialing return type нельзя:\n",
    "\n",
    "```c++\n",
    "std::shared_ptr<NetworkSubsystem> create_network();\n",
    "std::unique_ptr<ResourcesSubsystem> create_resources();\n",
    "\n",
    "template<typename Factory>\n",
    "auto create_subsystem(const Factory& factory, const char* name) -> decltype(factory())\n",
    "{\n",
    "    if (is_subsystem_enabled(name))\n",
    "        return factory();  // std::shared_ptr / std::unqiue_ptr\n",
    "    \n",
    "    return nullptr;  // std::nullptr_t\n",
    "}\n",
    "```\n",
    "\n",
    "Без `-> decltype(factory())` код не скомпилируется, т.к. у разных `return` разный тип, компилятор автоматически тип не выведет."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Закинуть этот код на [godbot.org](https://godbolt.org) для демонстрации, показать:\n",
    "\n",
    "```c++\n",
    "#include <memory>\n",
    "\n",
    "auto f1()\n",
    "{\n",
    "    if (true)\n",
    "        return std::make_unique<int>(0);\n",
    "    return nullptr;\n",
    "}\n",
    "\n",
    "auto f2() -> decltype(std::make_unique<int>(0))\n",
    "{\n",
    "    if (true)\n",
    "        return std::make_unique<int>(0);\n",
    "    return nullptr;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### C++11: user-defined literals / C++14: user-defined literals for STL"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "https://en.cppreference.com/w/cpp/language/user_literal\n",
    "\n",
    "https://www.modernescpp.com/index.php/user-defined-literals\n",
    "\n",
    "> Allows integer, floating-point, character, and string literals to produce objects of user-defined type by defining a user-defined suffix."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Про user-defined literal говорят, что это способ совместить значение и единицы измерения.\n",
    "\n",
    "Например, такой код:\n",
    "\n",
    "```c++\n",
    "const float distance_1_m = 12.3f * 1000; // 12.3 km\n",
    "const float distance_2_m = 3.f / 100; // 3 cm\n",
    "const float distance_3_m = 45.f / 1000; // 45 mm\n",
    "```\n",
    "\n",
    "Хотелось бы писать вот так:\n",
    "\n",
    "```c++\n",
    "const float distance_1_m = 12.3_km;\n",
    "const float distance_2_m = 3_cm;\n",
    "const float distance_3_m = 45_mm;\n",
    "```\n",
    "\n",
    "И это возможно. Достаточно определить оператор, который нужно применять к литералу, когда к нему приписан префикс `_km` / `_cm` / `_mm`:\n",
    "\n",
    "```c++\n",
    "constexpr double operator\"\" _km(double value)\n",
    "{\n",
    "    return value * 1000.0;\n",
    "}\n",
    "\n",
    "constexpr double operator\"\" _m(double value)\n",
    "{\n",
    "    return value;\n",
    "}\n",
    "\n",
    "constexpr double operator\"\" _mm(double value)\n",
    "{\n",
    "    return value / 1000.0;\n",
    "}\n",
    "\n",
    "constexpr double operator\"\" _cm(double value)\n",
    "{\n",
    "    return value / 100.0;\n",
    "}\n",
    "\n",
    "constexpr double operator\"\" _inch(double value)\n",
    "{\n",
    "    return value * 0.0254;\n",
    "}\n",
    "```\n",
    "\n",
    "Ошибки единиц измерения, когда в программах сантиметры складывают с метрами, а градусы с радианами - довольно частое явление. user-defined literals помогают избежать части из них.\n",
    "\n",
    "Такой код будет работать корректно:\n",
    "\n",
    "```c++\n",
    "const double distance_m = 12.3_km + 200_m;\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Отступление**\n",
    "\n",
    "Чтобы ещё снизить вероятность ошибки, иногда делают следующий шаг: для представления расстояния `double` заменяется на специальный тип `Distance`:\n",
    "\n",
    "```c++\n",
    "class Distance\n",
    "{\n",
    "    double value_m;\n",
    "    \n",
    "    constexpr explicit Distance(const double i_value_m) : value_m(i_value_m) {}\n",
    "\n",
    "public:\n",
    "    constexpr static Distance from_meters(const double m) { return Distance(m); }\n",
    "    constexpr double to_meters() const noexcept { return value_m; }\n",
    "    constexpr friend Distance operator +(const Distance l, const Distance r) noexcept;\n",
    "    constexpr friend Distance operator *(const Distance l, const double r) noexcept;\n",
    "    constexpr friend Distance operator *(const double l, const Distance r) noexcept;\n",
    "    // заметьте:\n",
    "    //   + только между двумя Distance, double + Distance - такого оператора нет\n",
    "    //   * только между Distance и double, нельзя сделать Distance * Distance и получить Distance\n",
    "    ...;\n",
    "};\n",
    "\n",
    "constexpr Distance operator\"\" _km(double value)\n",
    "{\n",
    "    return Distance::from_meters(value * 1000.0);\n",
    "}\n",
    "\n",
    "constexpr Distance operator\"\" _m(double value)\n",
    "{\n",
    "    return Distance::from_meters(value);\n",
    "}\n",
    "```\n",
    "\n",
    "Тогда:\n",
    "\n",
    "```c++\n",
    "12.3_km + 200_m; // ok\n",
    "12.3_km * 2; // ok\n",
    "\n",
    "12.3_km + 200; // compile-time error:\n",
    "               //   с точки зрения компилятора не определён operator+ (Distance, double)\n",
    "               //   с точки зрения программиста, непонятны единицы измерения у числа 200\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "В стандартной библиотеке C++ имеются свои user-defined literals для некоторых типов.\n",
    "\n",
    "Например, для единиц измерения интервала времени:\n",
    "\n",
    "https://en.cppreference.com/w/cpp/chrono/duration\n",
    "\n",
    "```c++\n",
    "using namespace std::chrono_literals;\n",
    "\n",
    "const auto duration = 2h;\n",
    "const auto duration = 45min;\n",
    "const auto duration = 1s;\n",
    "const auto duration = 16ms;\n",
    "const auto duration = 1.0h + 53min;\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Но user-defined literals в стандартной библиотеке используются не только для задания единиц измерения, но и для строк:\n",
    "\n",
    "```c++\n",
    "const auto str1 = \"hello c++\";    // const char*\n",
    "const auto str2 = \"hello c++\"s;   // std::string\n",
    "const auto str3 = \"hello c++\"sv;  // std::string_view\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### C++11: static assertions / C++17 static assertions without message"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "https://en.cppreference.com/w/cpp/language/static_assert\n",
    "\n",
    "> Performs compile-time assertion checking"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`static_assert(condition, message)` - compile-time проверка условий. Если проверка не проходит, компиляция останавливается с ошибкой и выдаёт сообщение `message`. C С++17 `message` опционально, его можно опускать.\n",
    "\n",
    "Примеры использования:\n",
    "\n",
    "```c++\n",
    "#include <type_traits>\n",
    "\n",
    "// хотим разрешить создавать Point<T> только\n",
    "// для целых и вещественных типов T:\n",
    "template <typename T>\n",
    "struct Point\n",
    "{\n",
    "    static_assert(std::is_floating_point<T>::value || std::is_intergal<T>::value,\n",
    "                  \"Point<T> must be created only for floating or integer types\");\n",
    "    \n",
    "    T x;\n",
    "    T y;\n",
    "    T z;\n",
    "};\n",
    "\n",
    "// хотим сделать свой swap для ограниченного набора типов,\n",
    "// чтобы swap гарантированно не бросал исключения\n",
    "template <typename T>\n",
    "void swap(T& a, T& b) noexcept\n",
    "{\n",
    "    static_assert(std::is_copy_constructible<T>::value &&\n",
    "                  std::is_nothrow_copy_constructible<T>::value &&\n",
    "                  std::is_nothrow_copy_assignable<T>::value);\n",
    "\n",
    "    auto t = b;\n",
    "    b = a;\n",
    "    a = t;\n",
    "}\n",
    "// Вопрос: кто-нибудь помнит, как мы делали swap через SFINAE и зачем?\n",
    "\n",
    "// например, если код ниже содержит трюки, работающие\n",
    "// только для 64-битных указателей, можно добавить compile-time проверку:\n",
    "static_assert(sizeof(void*) == 8);\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Атрибуты: [[maybe_unused]], [[nodiscard]], [[deprecated]], [[fallthrough]], [[likely]], [[unlikely]] и др."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Полный список атрибутов:\n",
    "\n",
    "https://en.cppreference.com/w/cpp/language/attributes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### [[fallthrough]] (since C++17)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "https://en.cppreference.com/w/cpp/language/attributes/fallthrough"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "В таком коде компилятор должен кинуть warning, что не все ветки `switch` заканчиваются `break;`.\n",
    "\n",
    "```c++\n",
    "switch (message.log_level)\n",
    "{\n",
    "    // ignore debug and info logs\n",
    "    case LogLevel::Debug:\n",
    "    case LogLevel::Info:\n",
    "        break;\n",
    "\n",
    "    // process warning and errors\n",
    "    case LogLevel::Warning:\n",
    "    case LogLevel::Error:\n",
    "        process_log_message(message.content);\n",
    "        break;\n",
    "};\n",
    "```\n",
    "\n",
    "Это корректный код, но из-за того, что программисты часто делали ошибки, забывая писать `break;`, разработчики компиляторов добавили соответствующий warning."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`[[fallthrough]]` - способ программиста сказать компилятору \"да, я уверен, здесь намеренно пропущен `break`\"."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Код ниже решает аналогичную задачу, но без warning-ов:\n",
    "\n",
    "\n",
    "```c++\n",
    "switch (message.log_level)\n",
    "{\n",
    "    // ignore debug and info logs\n",
    "    case LogLevel::Debug:\n",
    "        [[fallthrough]];\n",
    "    case LogLevel::Info:\n",
    "        break;\n",
    "\n",
    "    // process warning and errors\n",
    "    case LogLevel::Warning:\n",
    "        [[fallthrough]];\n",
    "    case LogLevel::Error:\n",
    "        process_log_message(message.content);\n",
    "        break;\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`[[fallthrough]]`, за которым по графу выполнения не идёт другого `case`/`default` - ill-formed-выражение. Скорее всего, не скомпилируется."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Более интересный пример:\n",
    "\n",
    "```c++\n",
    "switch (message.log_level)\n",
    "{\n",
    "    // ignore debug and info logs\n",
    "    case LogLevel::Debug:\n",
    "        [[fallthrough]];\n",
    "    case LogLevel::Info:\n",
    "         if (message.important)\n",
    "            [[fallthrough]];\n",
    "         else\n",
    "            break;\n",
    "\n",
    "    // process warning and errors\n",
    "    case LogLevel::Warning:\n",
    "        [[fallthrough]];\n",
    "    case LogLevel::Error:\n",
    "        process_log_message(message.content);\n",
    "        break;\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### [[deprecated]] (since C++14)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "https://en.cppreference.com/w/cpp/language/attributes/deprecated"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`[[deprecated]]` - сравнительно мягкий способ избавления от устаревшего публичного кода. Использование сущностей, помеченных как `deprecated`, генерирует warning.\n",
    "\n",
    "План избавления от устаревшего кода:\n",
    "1. Рассказываем всем, что в будущем планируем этот код удалить (и говорим, когда)\n",
    "2. Помечаем его `[[deprecated]]` - у пользователей кода (других программистов) начинают сыпаться warning-и\n",
    "3. Надеемся, что они эти warning-и в течение года-двух таки пофиксят\n",
    "4. Удаляем устаревший код\n",
    "5. У тех, кто не пофиксил warning-и, сломалась компиляция (но у них хотя бы было время, и компилятор их предупреждал)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Player\n",
    "{\n",
    "public:\n",
    "    [[deprecated(\"players import is going to be removed in release 15.4\")]]\n",
    "    Player(int id, const std::string& nickname, bool is_imported);\n",
    "\n",
    "    Player(int id, const std::string& nickname);\n",
    "    \n",
    "    ...\n",
    "    \n",
    "    [[deprecated(\"players import is going to be removed in release 15.4\")]]\n",
    "    bool is_imported() const noexcept;\n",
    "    \n",
    "    ...\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "[[deprecated(\"players import is going to be removed in release 15.4\")]]\n",
    "Player import_player();\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "В качестве байки: другой способ за-deprecate-ить код - sleeping constructor pattern."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Что можно пометить `[[deprecated]]`:\n",
    "* `class/struct/union`: `struct [[deprecated]] S;`\n",
    "* `typedef/using`:  `[[deprecated]] typedef S* PS;`, `using PS [[deprecated]] = S*;`\n",
    "* переменные (глобальные, static data): `[[deprecated]] int x;`\n",
    "* поле `class/struct/union`: `union U { [[deprecated]] int n; };`\n",
    "* функцию: `[[deprecated]] void f();`\n",
    "* `namespace`: `namespace [[deprecated]] NS { ... }`\n",
    "* enumeration: `enum [[deprecated]] E {};`\n",
    "* enumerator: `enum { A [[deprecated]], B [[deprecated]] = 42 };`\n",
    "* template specialization: `template<> struct [[deprecated]] X<int> {};`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### [[maybe_unused]] (since C++17)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "https://en.cppreference.com/w/cpp/language/attributes/maybe_unused"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`[[maybe_unused]]` - подсказка компилятору, что сущность может быть неиспользуемой, и это нормально. Подавляет warning-и о таких сущностях."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Пример**: неиспользуемый аргумент\n",
    "\n",
    "```c++\n",
    "class Animal\n",
    "{\n",
    "public:\n",
    "    virtual void say(bool loudly) = 0;\n",
    "};\n",
    "\n",
    "class Fish : public Animal\n",
    "{\n",
    "public:\n",
    "    void say([[maybe_unused]] bool loudly) override {}\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Пример**: warning зависит от режима компиляции (debug/release)\n",
    "\n",
    "```c++\n",
    "std::set<std::string> cities = ...;\n",
    "[[maybe_unused]] const int erased_count = cities.erase(\"Carthage\");\n",
    "assert(erased_count == 1);\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ещё `[[maybe_unused]]` можно навешивать на классы, поля класса, глобальные переменные, функции, `typedef`-ы, но практической пользы в этом мало."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### [[nodiscard]] (since C++17)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`[[nodiscard]]` - запрет программисту игнорировать возвращаемое значение функции."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "[[nodiscard]]\n",
    "int sqr(int x)\n",
    "{\n",
    "    return x * x;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Использование:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "int nine = sqr(3); // OK\n",
    "\n",
    "sqr(2); // compilation warning\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Рекомендации по использованию:\n",
    "1. _Рекомендаций от сообщества нет_\n",
    "2. Стоит помечать функции `[[nodiscard]]`, если вы уверены, что игнорирование возвращаемого значения выглядит как неестественное использование или ошибка"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Пример 1**: когда `nodiscard` поможет отловить ошибки\n",
    "\n",
    "```c++\n",
    "// класс immutable множества\n",
    "class frozenset\n",
    "{\n",
    "    ...\n",
    "\n",
    "    // return the new set having this with |value| added\n",
    "    [[nodiscard]] frozenset add(int value) const;\n",
    "};\n",
    "\n",
    "frozenset s = {1, 2, 3};\n",
    "s.add(4);  // warning\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Пример 2**: когда `nodiscard` поможет отловить ошибки\n",
    "\n",
    "```c++\n",
    "// класс динамического массива из стандартной библиотеки\n",
    "class vector\n",
    "{\n",
    "    ...\n",
    "    // пуст ли массив\n",
    "    [[nodiscard]] bool empty() const;\n",
    "};\n",
    "\n",
    "// класс динамического массива из Unreal Engine 4\n",
    "class TArray\n",
    "{\n",
    "    ...\n",
    "    // очистить\n",
    "    void Empty();\n",
    "};\n",
    "\n",
    "v.empty();  // чего хотел программист Unreal Engine 4? Проверить на пустоту или очистить массив?\n",
    "            // warning: unused return value\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__Замечание__: `std::vector::empty` помечен `[[nodiscard]]` начиная с С++20"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Пример 3**: когда `nodiscard` носит скорее рекомендательных характер - поможет отловить мелкие глупости\n",
    "\n",
    "```c++\n",
    "class Person\n",
    "{\n",
    "public:\n",
    "    [[nodiscard]]\n",
    "    Person(string name, string surname);\n",
    "    \n",
    "    [[nodiscard]]\n",
    "    const string& name() const noexcept { return name_; }\n",
    "    \n",
    "    [[nodiscard]]\n",
    "    const string& surname() const noexcept { return surname_; }\n",
    "\n",
    "private:\n",
    "    string name_;\n",
    "    string surname_;\n",
    "};\n",
    "\n",
    "\n",
    "Person(\"Dobrynia\", \"Nikitich\");  // warning\n",
    "\n",
    "Person p{\"Dobrynia\", \"Nikitich\"};\n",
    "p.name();  // warning\n",
    "\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Пример 4**: когда `nodiscard` поможет отловить проблемы лишних вычислений (для performance)\n",
    "\n",
    "```c++\n",
    "[[nodiscard]]\n",
    "string build_full_name(const Person& p)\n",
    "{\n",
    "    return p.name + ' ' + p.surname;\n",
    "}\n",
    "\n",
    "\n",
    "Person p{\"Dobrynia\", \"Nikittich\"};\n",
    "build_full_name(p);  // warning\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Пример 5**: когда `nodiscard` - спорный\n",
    "\n",
    "```c++\n",
    "// return success / failure indicator\n",
    "bool send_logs_to_server(const string& logs_folder);\n",
    "```\n",
    "\n",
    "Не всегда программист обязан обрабатывать ошибки алгоритмов.\n",
    "\n",
    "Например, для функции отправки логов на сервер:\n",
    "* production код может игнорировать факт ошибки\n",
    "* unit tests код может использовать метку об ошибке для тестирования \n",
    "\n",
    "Можно проигнорировать возвращаемое значение:\n",
    "\n",
    "```c++\n",
    "bool send_logs_to_server(const string& logs_folder);\n",
    "\n",
    "send_logs_to_server(\"logs/\");\n",
    "```\n",
    "\n",
    "а можно заставить пользователя функции явно обозначать, что ему код ошибки не интересен:\n",
    "\n",
    "```c++\n",
    "[[nodiscard]]\n",
    "bool send_logs_to_server(const string& logs_folder);\n",
    "\n",
    "[[maybe_unused]] bool are_logs_sent =\n",
    "    send_logs_to_server(\"logs/\");\n",
    "```\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### [[likely]], [[unlinkely]] (since C++20)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "https://en.cppreference.com/w/cpp/language/attributes/likely"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`[[likely]], [[unlinkely]]` - стандартизированный способ сказать компилятору, что ветка `if`/`switch` будет выполняться с большей/меньшей вероятностью."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "void game_loop()\n",
    "{\n",
    "    while (true)\n",
    "    {\n",
    "        auto& player_controller = get_player_controller();\n",
    "        \n",
    "        [[unlikely]] if (player_controller.is_player_dead())\n",
    "        {\n",
    "            finish_game();\n",
    "            return;\n",
    "        }\n",
    "\n",
    "        ...\n",
    "    }\n",
    "}\n",
    "```\n",
    "\n",
    "**Вопрос**: зачем их ввели?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Доклад на cppcon про likely/unlikely, что они:\n",
    "* изменяют layout кода, группируя \"горячие\" пути  вместе\n",
    "* вступают в конфликт с имеющимися эвристиками определения горячих/холодных путей внутри компилятора, поэтому могут скорее навредить (_\"[[unlikely]] is more likely than no [[unlikely]]\"_)\n",
    "\n",
    "[C++20’s likely Attribute - Optimizations, Pessimizations, and unlikely Consequences - CppCon](https://www.youtube.com/watch?v=RjPK3HKcouA)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### C++17 string_view"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "https://en.cppreference.com/w/cpp/string/basic_string_view"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`std::string_view` - невладеющая обёртка над массивом символов. Содержит указатель на начало и размер.\n",
    "\n",
    "**Вопросы**:\n",
    "* чему равен `sizeof(std::string_view)`?\n",
    "* как `std::string_view` передавать в функции?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`std::string_view` ввели чтобы решить проблему перегруженности интерфейсов.\n",
    "\n",
    "Например, выдуманная библиотечная функция `int convert_to_int(<строка>)` для удобства использования должна иметь 3 перегрузки:\n",
    "\n",
    "```c++\n",
    "// 1\n",
    "int convert_to_int(const std::string& s);\n",
    "\n",
    "// 2\n",
    "int convert_to_int(const char* s);\n",
    "\n",
    "// 3\n",
    "int convert_to_int(const char* s, std::size_t n);\n",
    "```\n",
    "\n",
    "**Вопрос:**\n",
    "* чем вариант 3 отличается от 2?\n",
    "* в каких случаях можно пользоваться только вариантом 3, а вариант 2 не сработает?\n",
    "* в каких случаях вариант 2 удобнее и быстрее чем варинт 3?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Варианты вызова `convert_to_int`:\n",
    "\n",
    "```c++\n",
    "// 1\n",
    "std::string s = read_user_input();\n",
    "int x = convert_to_int(s);\n",
    "\n",
    "// 2\n",
    "convert_to_int(\"123\");\n",
    "\n",
    "// 3\n",
    "std::string json = read_json();  // и как-то мы знаем, что с 12-го по 16-ый лежит число\n",
    "convert_to_int(&json[12], 5);\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`std::string_view` позволяет сохранить все возможные варианты вызова, но оставить один интерфейс:\n",
    "\n",
    "```c++\n",
    "int convert_to_int(std::string_view sv);\n",
    "```\n",
    "\n",
    "тогда:\n",
    "\n",
    "```c++\n",
    "// 1\n",
    "//\n",
    "// создаётся временный std::string_view, который инициализируется\n",
    "// адресом s.c_str() и размером s.size().\n",
    "//\n",
    "// std::string_view корректен, пока жив s. Обазянность за отслеживанием\n",
    "// времени жизни массива, на который ссылается std::string_view, лежит\n",
    "// на программисте.\n",
    "std::string s = read_user_input();\n",
    "int x = convert_to_int(s);\n",
    "\n",
    "// 2\n",
    "//\n",
    "// создаётся временный std::string_view, который инициализируется\n",
    "// адресом s и размером strlen(s) - в момент создания конструктор\n",
    "// std::string_view будет узнавать длину строки (!)\n",
    "convert_to_int(\"123\");\n",
    "\n",
    "// 3\n",
    "//\n",
    "// явно указываем, на какой массив ссылается std::string_view\n",
    "std::string json = read_json();  // и как-то мы знаем, что с 12-го по 16-ый лежит число\n",
    "convert_to_int(std::string_view{&json[12], 5});\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Замечание:** свойство, что `std::string_view` обязан хранить длину, может быть как полезным, так и вредным:\n",
    "\n",
    "вот такой код будет работать медленнее чем мог бы на обычном `const char *` (почему?):\n",
    "\n",
    "```c++\n",
    "bool is_male(const std::string_view fullname)\n",
    "{\n",
    "    return fullname.starts_with(\"Mr.\");\n",
    "}\n",
    "\n",
    "is_male(\"Mr. Bond. James Bond.\");\n",
    "is_male(\"Mr. Antoine Marie Jean-Baptiste Roger, comte de Saint-Exupéry\");\n",
    "```\n",
    "\n",
    "<br />\n",
    "\n",
    "А такой код через `std::string_view` может работать быстрее (почему?):\n",
    "\n",
    "```c++\n",
    "std::stringstream ss;\n",
    "for (int i = 10; i != 1; --i)\n",
    "    ss << i << \" green bottles hanging on the wall. And if one green bottle should accidentally fall...\\n\";\n",
    "```\n",
    "\n",
    "```c++\n",
    "constexpr std::string_view msg =\n",
    "    \" green bottles hanging on the wall. And if one green bottle should accidentally fall...\\n\";\n",
    "std::stringstream ss;\n",
    "for (int i = 10; i != 1; --i)\n",
    "    ss << i << msg;\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### std::span (since C++20)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "https://en.cppreference.com/w/cpp/container/span"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Идея, стоящая за `std::span` аналогична `std::string_view` - невладеющий view на массив (или часть массива).\n",
    "\n",
    "С `std::span` комитет пошёл чуть дальше, и добавил возможности для оптимизаций, когда размер данных известен во время компиляции."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Пример использования:\n",
    "\n",
    "```c++\n",
    "double sum(const std::span<double> v)\n",
    "{\n",
    "    double rv = 0.0;\n",
    "    for (double x : v)\n",
    "        rv += x;\n",
    "    return rv;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "За оптимизации для известных размеров отвечает второй шаблонный параметр:\n",
    "\n",
    "```c++\n",
    "template< \n",
    "    class T, \n",
    "    std::size_t Extent = std::dynamic_extent\n",
    "> class span;\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Вопрос на понимание**:\n",
    "\n",
    "* чему равен `sizeof(std::span<double>)`?\n",
    "* чему равен `sizeof(std::span<double, 5>)`?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### heterogeneous lookup (ordered map/set since C++14, unordered map/set since C++20)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "https://www.bfilipek.com/2019/05/heterogeneous-lookup-cpp14.html\n",
    "\n",
    "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0919r2.html"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Сначала рассмотрим мотивацию для новой фичи:\n",
    "\n",
    "```c++\n",
    "std::map<std::string, int> name_to_age = {\n",
    "    {\"Ilya Muromec\", 42},\n",
    "    {\"Dobrynia Nikitich\", 57},\n",
    "    {\"Alesha Popovich\", 23}\n",
    "};\n",
    "\n",
    "if (name_to_age.find(\"Koshei Bessmertniy\") == name_to_age.end())\n",
    "    std::cout << \"how somebody can be too old?\" << std::endl;\n",
    "```\n",
    "\n",
    "**Вопрос**: В этом примере ошибок нет (вроде бы), но какая проблема в нём скрыта?\n",
    "\n",
    "<details>\n",
    "<summary>ответ</summary>\n",
    "<details>\n",
    "<p>\n",
    "\n",
    "Чтобы поискать значение в `std::map` создаётся промежуточный объект `std::string`. В случае, если он не влезает в SSO, это дорого: нужна аллокация.\n",
    "\n",
    "</p>\n",
    "</details>\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "heterogeneous lookup - трюк, который чинит проблему:\n",
    "\n",
    "```c++\n",
    "std::map<std::string, int>              name_to_age; // контейнер со стандартным поиском\n",
    "std::map<std::string, int, std::less<>> name_to_age; // контейнер с \"гетерогенным\" поиском\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Трюк достаточно просто использовать, но как он работает?\n",
    "\n",
    "Строчки выше компилятор разворачивает в следующее:\n",
    "\n",
    "```c++\n",
    "std::map<std::string, int, std::less<std::string>> name_to_age; // контейнер со стандартным поиском\n",
    "std::map<std::string, int, std::less<>>            name_to_age; // контейнер с \"гетерогенным\" поиском\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Классы-компараторы в стандартной библиотеке выглядят так (примерно и упрощённо для иллюстарции идеи, на самом деле всё много сложнее):\n",
    "\n",
    "```c++\n",
    "// для конкретного типа (в том числе std::string)\n",
    "template<typename T>\n",
    "struct less {\n",
    "    bool operator < (const T& lhs, const T& rhs) const {\n",
    "        return lhs < rhs;\n",
    "    }\n",
    "};\n",
    "\n",
    "// специализация для T=void\n",
    "template<>\n",
    "struct less<void> {\n",
    "    using is_transparent = void;\n",
    "    \n",
    "    template<typename T1, typename T2>\n",
    "    bool operator < (const T1& lhs, const T2& rhs) const {\n",
    "        return lhs < rhs;\n",
    "    }\n",
    "};\n",
    "```\n",
    "\n",
    "**Вопрос**: в каком месте требуется конструирование `std::string` для стандартного поиска, и почему можно его избежать в гетерогенном варианте?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Осталось дело за малым - скомпилировать transparent-вариант поиска, если он доступен с переданными типами аргументов.\n",
    "\n",
    "В libc++ это делается так:\n",
    "\n",
    "```c++\n",
    "iterator find(const key_type& __k)             {return __tree_.find(__k);}\n",
    "\n",
    "template <typename _K2>\n",
    "typename enable_if<__is_transparent<_Compare, _K2>::value, iterator>::type\n",
    "find(const _K2& __k)                           {return __tree_.find(__k);}\n",
    "```\n",
    "\n",
    "В listdc++ так:\n",
    "\n",
    "```c++\n",
    "iterator find(const key_type& __x) { return iterator(_Base::find(__x), this); }\n",
    "\n",
    "template<typename _Kt,\n",
    "         typename _Req = typename __has_is_transparent<_Compare, _Kt>::type>\n",
    "iterator find(const _Kt& __x)      { return { _Base::find(__x), this }; }\n",
    "```\n",
    "\n",
    "**Вопрос**: как это работает?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### init-if/switch (since C++ 17)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Синтаксический сахар для сокращения кода:\n",
    "\n",
    "```c++\n",
    "if (init-statement; condition)\n",
    "    ...\n",
    "else\n",
    "    ...\n",
    "```\n",
    "\n",
    "До С++17:\n",
    "\n",
    "```c++\n",
    "auto& network_system = NetworkSystem::get(app_context);\n",
    "if (network_system.is_enabled())\n",
    "    network_system.post(logs_data);\n",
    "```\n",
    "\n",
    "После С++17:\n",
    "\n",
    "```c++\n",
    "if (auto& network_system = NetworkSystem::get(app_context); network_system.is_enabled())\n",
    "    network_system.post(logs_data);\n",
    "```\n",
    "\n",
    "Переменная, инициализированная в `init-statement`, доступна и в `else`-блоке:\n",
    "\n",
    "```c++\n",
    "if (auto& network_system = NetworkSystem::get(app_context); network_system.is_enabled())\n",
    "    network_system.post(logs_data);\n",
    "else\n",
    "    std::cout << \"network is disabled, reason: \" << network_system.disabled_reason() << std::endl;\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### slicing ordered/unordered map/set (since C++17)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Рассмотрим пример - мотивацию.\n",
    "\n",
    "Задача - перекладывать элементы из одного отображения в другое в процессе обработки. Нас интересует только процесс перекладывания. Псевдокод:\n",
    "\n",
    "```c++\n",
    "std::map<int, std::string> id_to_name = /*...*/;  // набор элементов для обработки\n",
    "std::map<int, std::string> id_to_name_processed;  // уже обработанные элементы\n",
    "\n",
    "while (true)\n",
    "{\n",
    "    const int id = take_id_to_process();\n",
    "    const bool ok = process_item(id);\n",
    "    if (!ok)\n",
    "        break;\n",
    "    \n",
    "    // HERE: transfer from unprocessed to processed\n",
    "    id_to_name_processed[id] = id_to_name[id];\n",
    "    id_to_name.remove(id);\n",
    "}\n",
    "```\n",
    "\n",
    "**Вопрос**: как устроен `std::map` внутри? какая работа будет происходить в процессе переноса?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Можно ускорить процесс переноса элементов между map/set одинакового типа, вычленяя из них сами ноды и вставляя ноды в результат:\n",
    "\n",
    "Было:\n",
    "\n",
    "```c++\n",
    "id_to_name_processed[id] = id_to_name[id];\n",
    "id_to_name.remove(id);\n",
    "```\n",
    "\n",
    "Стало:\n",
    "\n",
    "```c++\n",
    "id_to_name_processed.insert(\n",
    "    id_to_name.extract(id));\n",
    "```\n",
    "\n",
    "Или так:\n",
    "\n",
    "```c++\n",
    "id_to_name_processed.insert(\n",
    "    id_to_name.extract(\n",
    "        id_to_name.find(id)));\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Другой рабочий вариант slicing - алгоритм `merge`, который внутри заоптимизирован на перекидывание нод контейнера:\n",
    "\n",
    "```c++\n",
    "std::set<int> src = {1, 3, 5};\n",
    "std::set<int> dst = {2, 4, 5};\n",
    "dst.merge(src);\n",
    "// src == { 5 }\n",
    "// dst == { 1, 2, 3, 4, 5 }\n",
    "```\n",
    "\n",
    "Пример использования - три множества людей соберём в одно:\n",
    "\n",
    "```c++\n",
    "std::set<Person> regiment_l = get_left_hand_regiment();\n",
    "std::set<Person> regiment_c = get_big_regiment();\n",
    "std::set<Person> regiment_r = get_right_hand_regiment();\n",
    "\n",
    "std::set<Person> army = std::move(regiment_l);\n",
    "army.merge(regiment_c);\n",
    "army.merge(regiment_r);\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### std::optional (since C++17)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "https://en.cppreference.com/w/cpp/utility/optional"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`std::optional<T>` - класс для хранения опционально присутствющего значения.\n",
    "\n",
    "Основные функции:\n",
    "* можно спросить `has_value()` - есть ли значение\n",
    "* можно запросить само значение через `get()` или `operator->()`\n",
    "* в своём деструкторе позовёт деструктор у значения, если оно есть\n",
    "* значение хранится в лаяуте класса (т.е. сам `std::optional` свободен от аллокаций)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Очень простой и очень полезный класс.\n",
    "\n",
    "Сценарии применения:\n",
    "\n",
    "1. возврат результата из функции, когда результат может отсутствовать:\n",
    "\n",
    "```c++\n",
    "std::optional<Point3D>  get_intersection(Line3D l1, Line3D l2);\n",
    "```\n",
    "\n",
    "2. опциональное поле у класса либо поле с ленивой инициализацией\n",
    "\n",
    "```c++\n",
    "class SalesmanWidget\n",
    "{\n",
    "    ...\n",
    "\n",
    "    // optional, потому что может быть не запущено задачи\n",
    "    std::optional<ProblemHandler> handler;  // не требует аллокаций в отличие от std::unique_ptr\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Как НЕ надо применять `std::optional`:\n",
    "            \n",
    "1. `std::optional<bool>` - см. `boost::tribool`\n",
    "2. как опциональный аргумент:\n",
    "\n",
    "```c++\n",
    "void func(std::optional<int> arg);\n",
    "```\n",
    "\n",
    "**Вопрос**: а почему так не надо?\n",
    "\n",
    "**Вопрос**: чему равен `sizeof(std::optional<int>)`?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### structured bindings (since C++17)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "structured bindings - синтаксический сахар - автоматическая распаковка структур и кортежей по именам."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Сравните, насколько structured bindings украшают итерирование по отображениям:\n",
    "\n",
    "```c++\n",
    "std::unordered_map<int, string> id_to_name = /* ... */;\n",
    "\n",
    "// before C++17\n",
    "for (const auto& id_and_name : id_to_name)\n",
    "{\n",
    "    const auto& id = id_and_name.first;\n",
    "    const auto& name = id_and_name.second;\n",
    "    /* process id and name */\n",
    "}\n",
    "    \n",
    "// C++17:\n",
    "for (const auto& [id, name]: id_to_name)\n",
    "{\n",
    "    /* process id and name */\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Или как structured bindings позволяют распаковать структуру:\n",
    "\n",
    "```c++\n",
    "struct Point\n",
    "{\n",
    "    float x, y, z;\n",
    "};\n",
    "\n",
    "std::vector<Point> points = /* ... */;\n",
    "\n",
    "// before C++17:\n",
    "for (const auto p : points)\n",
    "    std::cout << p.x * p.x + p.y * p.y + p.z * p.z << std::endl;\n",
    "\n",
    "// C++17:\n",
    "for (const auto [x, y, z] : points)\n",
    "    std::cout << x * x + y * y + z * z << std::endl;\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Или так:\n",
    "\n",
    "```c++\n",
    "struct Person\n",
    "{\n",
    "    std::string name;\n",
    "    std::string surname;\n",
    "    int age;\n",
    "};\n",
    "\n",
    "// before C++17:\n",
    "const Person person = get_friend();\n",
    "std::cout << person.name + \" \" + person.surname << std::endl;\n",
    "\n",
    "// C++17:\n",
    "const auto [name, surname, _] = get_friend();\n",
    "std::cout << name + \" \" + surname << std::endl;\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Как делать structured bindings доступными для классов с приватными полями - было на лекции по `constexpr`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Давайте посмотрим, что происходит внутри structured binding:\n",
    "\n",
    "**Вопрос-наброс**: может ли компилятор в этом примере применить NRVO-оптимизацию?\n",
    "\n",
    "```c++\n",
    "struct Person\n",
    "{\n",
    "    std::string name;\n",
    "    std::string surname;\n",
    "    int age;\n",
    "};\n",
    "\n",
    "Person get_friend();\n",
    "\n",
    "std::string get_friend_name()\n",
    "{\n",
    "    auto [name, surname, age] = get_friend();\n",
    "    return name;   // <---------------------------- тут\n",
    "}\n",
    "```\n",
    "\n",
    "<details>\n",
    "<summary>ответ</summary>\n",
    "<p>\n",
    "\n",
    "Нет, компилятор скорее всего не сможет здесь применить NRVO. Когда работает structured bindings, создаётся полноценный объект `Person`, а затем создаются alias-ы на его поля. В `return name` для компилятора происходит доступ до поля структуры, он не может это поле разместить отдельно от структуры.\n",
    "\n",
    "</p>\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### using enum (C++ 20)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1099r4.html"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "using enum - синтаксический сахар для сокращения кода, чтобы поменьше писать имя `enum`.\n",
    "\n",
    "Заведём enum с ужасающе длинным названием:\n",
    "\n",
    "```c++\n",
    "enum class rgb_color_channel {\n",
    "    red,\n",
    "    green,\n",
    "    blue\n",
    "};\n",
    "```\n",
    "\n",
    "Напишем `switch` по нему в старом стиле:\n",
    "\n",
    "```c++\n",
    "// before C++20\n",
    "std::string_view to_string(rgba_color_channel channel) {\n",
    "  switch (channel) {\n",
    "    case rgb_color_channel::red:   return \"red\";\n",
    "    case rgb_color_channel::green: return \"green\";\n",
    "    case rgb_color_channel::blue:  return \"blue\";\n",
    "  }\n",
    "}\n",
    "```\n",
    "\n",
    "И в новом стиле:\n",
    "\n",
    "```c++\n",
    "// after C++20\n",
    "std::string_view to_string(rgba_color_channel channel) {\n",
    "  switch (my_channel) {\n",
    "    using enum rgb_color_channel;  // <- !!!\n",
    "    case red:   return \"red\";\n",
    "    case green: return \"green\";\n",
    "    case blue:  return \"blue\";\n",
    "  }\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "using enum можно использовать и в других местах:\n",
    "\n",
    "```c++\n",
    "// before C++20\n",
    "if (channel == rgb_color_channel::red)\n",
    "    std::cout << \"red\";\n",
    "```\n",
    "\n",
    "```c++\n",
    "// after C++ 20\n",
    "using enum rgb_color_channel;\n",
    "if (channel == red)\n",
    "    std::cout << \"red\";\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Прочие мелкие улучшения, не вошедшие в лекцию**:\n",
    "\n",
    "* [C++11: inline namespaces](https://github.com/AnthonyCalandra/modern-cpp-features#inline-namespaces)\n",
    "* [C++11: ref-qualified member functions](https://github.com/AnthonyCalandra/modern-cpp-features#ref-qualified-member-functions)\n",
    "* [C++14: variable templates](https://github.com/AnthonyCalandra/modern-cpp-features#variable-templates)\n",
    "* [C++17: std::filesystem](https://en.cppreference.com/w/cpp/filesystem)\n",
    "* [C++17: inline variables](https://github.com/AnthonyCalandra/modern-cpp-features#inline-variables)\n",
    "* [C++17: nested namespaces](https://github.com/AnthonyCalandra/modern-cpp-features#nested-namespaces)\n",
    "* [C++20: spaceship operator](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1614r1.html),\n",
    "  [Meeting C++. Jonathan Muler: Using C++20 three-way comparison](https://youtu.be/bysb-tzglqg)\n",
    "* [C++20: calendar + chrono improvements](https://en.cppreference.com/w/cpp/chrono)\n",
    "* [C++20: synchronization library improvements](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1135r5.html)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
